Appearance
2823. 深度对象筛选
ts
type JSONValue =
| null
| boolean
| number
| string
| JSONValue[]
| { [key: string]: JSONValue }
type Obj = Record<string, JSONValue> | Array<JSONValue>
const isObject = (obj: any) => typeof obj === 'object' && obj !== null
function deepFilter(obj: Obj, fn: Function): Obj | undefined {
if (!isObject(obj)) return fn(obj) ? obj : undefined
if (Array.isArray(obj)) {
let i = 0
while (i < obj.length) {
const val = deepFilter(obj[i] as any, fn)
if (val === undefined) {
obj.splice(i, 1) // 原地删除无效值
} else {
obj[i] = val
i++
}
}
return obj.length > 0 ? obj : undefined
}
for (const key in obj) {
const val = obj[key]
const filtered = deepFilter(val as any, fn)
if (filtered === undefined) {
delete obj[key]
} else {
obj[key] = filtered
}
}
return Object.keys(obj).length > 0 ? obj : undefined
}筛选与删除的传播
deepFilter 有一个重要设计:删除向上传播。
基本类型不满足 fn → 返回 undefined。对于父级来说,子属性返回 undefined 意味着应该删除这个子属性(对象则 delete,数组则 splice)。
更关键的是:如果所有子属性都被删除,父级本身也返回 undefined,导致更上层删除父级。这就是「空容器向上传播」——一个对象如果所有子属性都不满足条件,它自己也没有存在的意义。
数组的 while 循环
数组用 while 循环 + splice 而非 filter,因为需要原地修改。splice(i, 1) 删除元素后,索引 i 不递增——因为下一个元素会移到当前位置。只有当元素保留时才 i++。
这是在中原地删除元素的经典模式。如果用 for 循环,删除后索引管理容易出错。
边界
- 空数组
[]如果所有元素被筛掉 → 返回undefined - 空对象
{}→ 返回undefined - 筛选函数对容器的判断:
deepFilter不会用fn来判断容器本身(数组/对象),只判断基本类型和 null。容器通过「子元素是否都被筛光」这个间接标准来决定去留
